import SunCalc from 'suncalc'

type SunTickName = 'nadir'| 'nightEnd'| 'nauticalDawn'| 'dawn'| 'sunrise'| 'sunriseEnd'| 'goldenHourEnd'| 'solarNoon'| 'goldenHour'| 'sunsetStart'| 'sunset'| 'dusk'| 'nauticalDusk'| 'night'

type SunTicksOrigin = Record<SunTickName, Date>
type SunTicks = {name: SunTickName, time: Date}[]

type TimePhaseInfo = {
    current: {name: string, time: Date},
    next: {name: string, time: Date},
    previous: {name: string, time: Date},
    previousTick: {name: string, time: Date},
    nextTick: {name: string, time: Date}
}

const sunTickNameList: string[] = ['nadir', 'nightEnd', 'nauticalDawn', 'dawn',
    'sunrise', 'sunriseEnd', 'goldenHourEnd', 'solarNoon', 'goldenHour',
    'sunsetStart', 'sunset', 'dusk', 'nauticalDusk', 'night']

const timeTickNameList: string[] = [
    '午夜', '夜间结束', '航海黎明', '黎明',
    '日出开始', '日出结束', '黄金时刻结束', '正午', '黄金时刻开始',
    '日落开始', '日落结束', '黄昏', '航海黄昏', '夜间开始'
]

const timePhaseNameList: string[] = [
    '下半夜', '天文曙光', '航海曙光', '民用曙光', '日出', '黄金时刻', '上午',
    '下午', '黄金时刻', '日落', '民用暮光', '航海暮光', '天文暮光', '上半夜'
]

export function getSunTicks(latitude: number, longitude: number, time:Date): SunTicks {
    const times: SunTicksOrigin = SunCalc.getTimes(time, latitude, longitude)

    // sort the key by time
    const sortedKeys = Object.keys(times) as SunTickName[];
    sortedKeys.sort((a, b) => sunTickNameList.indexOf(a) - sunTickNameList.indexOf(b))
    const sortedTimes: SunTicks = sortedKeys.map(key => ({name: key, time: times[key]}))

    if (sortedTimes[1].time < time){
        // 取明日的nadir时间
        const nextDayTimes = SunCalc.getTimes(new Date(time.getTime() + 24 * 60 * 60 * 1000), latitude, longitude)
        const nextDaySortedKeys = Object.keys(nextDayTimes) as SunTickName[];
        nextDaySortedKeys.sort((a, b) => sunTickNameList.indexOf(a) - sunTickNameList.indexOf(b))
        const nextDaySortedTimes: SunTicks = nextDaySortedKeys.map(key => ({name: key, time: nextDayTimes[key]}))
        sortedTimes[0].time = nextDaySortedTimes[0].time; // 更新nadir时间为明日的nadir时间
    }

    return sortedTimes
}


export function currentTimePhase(sunTimes: SunTicks): TimePhaseInfo {
    const now = new Date()

    // 找出现在在哪两个时间点之间
    for (let i = 0; i < sunTimes.length - 1; i++) {
        const start = sunTimes[i].time
        const end = sunTimes[i + 1].time

        if (now >= start && now < end) {
            return {
                current: {name: timePhaseNameList[i], time: now},
                next: {name: i < sunTimes.length - 1 ? timePhaseNameList[i + 1] : timePhaseNameList[0],
                    time: end},
                previous: {name: i > 0 ? timePhaseNameList[i - 1] : timePhaseNameList[timePhaseNameList.length - 1],
                    time: start},
                previousTick: {name: timeTickNameList[i], time: start},
                nextTick: {name: timeTickNameList[i + 1], time: end}
            }
        }
    }

    return {
        current: {name: timePhaseNameList[timePhaseNameList.length - 1], time: now},
        next: {name: timePhaseNameList[0], time: sunTimes[0].time},
        previous: {name: timePhaseNameList[timePhaseNameList.length - 2], time: sunTimes[sunTimes.length - 1].time},
        previousTick: {name: timeTickNameList[timeTickNameList.length - 1], time: sunTimes[sunTimes.length - 1].time},
        nextTick: {name: timeTickNameList[0], time: sunTimes[0].time}
    }
}


export function getSunPos(latitude: number, longitude: number, time: Date){
    const pos: {altitude:number, azimuth: number} = SunCalc.getPosition(time, latitude, longitude)
    return {
        altitude: pos.altitude , // 转换为度
        azimuth: pos.azimuth  // 转换为度
    }
}

export function getMoonPos(latitude: number, longitude: number, time: Date){
    const pos: {altitude:number, azimuth: number} = SunCalc.getMoonPosition(time, latitude, longitude)
    return {
        altitude: pos.altitude , // 转换为度
        azimuth: pos.azimuth // 转换为度
    }
}

// =============== HIP 恒星数据 ================

export interface HipData {
    RA: number[],
    DEC: number[],
    Mag: number[]
}


// Use a URL-friendly approach for the binary file
const hipData = new URL('../assets/hip.bin', import.meta.url).href;

export async function getHipData() {
    // HIP的二进制文件，格式为：
    // [uint16: 数据长度][float32: RA_offset][float32: RA_factor]
    // [float32: DEC_offset][float32: DEC_factor][float32: Mag_offset][float32: Mag_factor]
    // [uint16*n: RA][uint16*n: DEC][uint16*n: Mag]
    const response = await fetch(hipData)
    if (!response.ok) {
        throw new Error('Failed to fetch HIP data')
    }
    const buffer = await response.arrayBuffer()
    const view = new DataView(buffer)
    const dataLength = view.getUint16(0, true)
    const RA_offset = view.getFloat32(2, true)
    const RA_factor = view.getFloat32(6, true)
    const DEC_offset = view.getFloat32(10, true)
    const DEC_factor = view.getFloat32(14, true)
    const Mag_offset = view.getFloat32(18, true)
    const Mag_factor = view.getFloat32(22, true)
    const RA: number[] = []
    const DEC: number[] = []
    const Mag: number[] = []
    const offset_start = 26; // 数据起始位置
    const data_offset = dataLength * 2;

    for (let i = 0; i < dataLength; i++) {
        RA.push(view.getUint16(offset_start + i * 2, true) / RA_factor + RA_offset);
        DEC.push(view.getUint16(offset_start + data_offset + i * 2, true) / DEC_factor + DEC_offset);
        Mag.push(view.getUint16(offset_start + data_offset * 2 + i * 2, true) / Mag_factor + Mag_offset);
    }
    return {
        RA: RA,
        DEC: DEC,
        Mag: Mag
    } as HipData;
}

export interface HipDataExtend extends HipData {
    alt: number[],
    azi: number[],
}

import { JulDay, LM_Sidereal_Time, re2azi } from './calc'

export function calHipAltAzi(hipData: HipData, time: Date, lat: number, lon: number): HipDataExtend{
    const jd = JulDay(time)
    const st = LM_Sidereal_Time(jd, lon)
    lat = lat * Math.PI / 180 // 纬度，单位弧度

    const hipDataExtend: HipDataExtend = {
        RA: hipData.RA,
        DEC: hipData.DEC,
        Mag: hipData.Mag,
        alt: [],
        azi: []
    }

    for(let i = 0; i < hipData.RA.length;i++){
        const pos = re2azi(st, lat, hipData.RA[i], hipData.DEC[i])
        hipDataExtend.alt.push(pos.alt) // 高度
        hipDataExtend.azi.push(pos.azi) // 方位角
    }
    return hipDataExtend
}


export interface RawMoonPhase {
    fraction: number; // 月亮照明度
    angle: number; // 月亮相位角
    phase: number; // 月亮相位名称
}

export function getMoonPhase(time: Date){
    const i = SunCalc.getMoonIllumination(time) as RawMoonPhase;
    return i
}

export interface MoonTick {
    rise: Date,
    set: Date,
    alwaysUp: boolean,
    alwaysDown: boolean
}
export function getMoonTime(latitude: number, longitude: number, date: Date) {
    const i = SunCalc.getMoonTimes(/*Date*/ date, /*Number*/ latitude, /*Number*/ longitude) as MoonTick;
    return i
}